//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#include <ddk.h>
#include <xllp_lcd.h>
#include <bulverde.h>
#include <drvpub.h>
#include "display_def.h"

#if 0
void DumpLcdControllerReg()
{
    kprintf("LCCR0  = 0x%08x\n", LCCR0);
    kprintf("LCCR1  = 0x%08x\n", LCCR1);
    kprintf("LCCR2  = 0x%08x\n", LCCR2);
    kprintf("LCCR3  = 0x%08x\n", LCCR3);
    kprintf("LCCR4  = 0x%08x\n", LCCR4);
    kprintf("LCCR5  = 0x%08x\n", LCCR5);

    kprintf("OVL1C1 = 0x%08x\n", OVL1C1);
    kprintf("OVL1C2 = 0x%08x\n", OVL1C2);
    kprintf("OVL2C1 = 0x%08x\n", OVL2C1);
    kprintf("OVL2C2 = 0x%08x\n", OVL1C2);

    kprintf("CCR    = 0x%08x\n", CCR);
    kprintf("CMDCR  = 0x%08x\n", CMDCR);
    kprintf("TRGBR  = 0x%08x\n", TRGBR);
    kprintf("TCR    = 0x%08x\n", TCR);

    kprintf("FDADR0 = 0x%08x\n", FDADR0);
    kprintf("FDADR1 = 0x%08x\n", FDADR1);
    kprintf("FDADR2 = 0x%08x\n", FDADR2);
    kprintf("FDADR3 = 0x%08x\n", FDADR3);
    kprintf("FDADR4 = 0x%08x\n", FDADR4);
    kprintf("FDADR5 = 0x%08x\n", FDADR5);
    kprintf("FDADR6 = 0x%08x\n", FDADR6);

    kprintf("FBR0   = 0x%08x\n", FBR0);
    kprintf("FBR1   = 0x%08x\n", FBR1);
    kprintf("FBR2   = 0x%08x\n", FBR2);
    kprintf("FBR3   = 0x%08x\n", FBR3);
    kprintf("FBR4   = 0x%08x\n", FBR4);
    kprintf("FBR5   = 0x%08x\n", FBR5);
    kprintf("FBR6   = 0x%08x\n", FBR6);

//    kprintf("LCDBSCNTR = 0x%08x\n", LCDBSCNTR);
    kprintf("PRSR   = 0x%08x\n", PRSR);

    kprintf("LCSR0  = 0x%08x\n", LCSR0);
    kprintf("LCSR1  = 0x%08x\n", LCSR1);
    kprintf("LIIDR  = 0x%08x\n", LIIDR);

    kprintf("FSADR0 = 0x%08x\n", FSADR0);
    kprintf("FSADR1 = 0x%08x\n", FSADR1);
    kprintf("FSADR2 = 0x%08x\n", FSADR2);
    kprintf("FSADR3 = 0x%08x\n", FSADR3);
    kprintf("FSADR4 = 0x%08x\n", FSADR4);
    kprintf("FSADR5 = 0x%08x\n", FSADR5);
    kprintf("FSADR6 = 0x%08x\n", FSADR6);

    kprintf("FIDR0  = 0x%08x\n", FIDR0);
    kprintf("FIDR1  = 0x%08x\n", FIDR1);
    kprintf("FIDR2  = 0x%08x\n", FIDR2);
    kprintf("FIDR3  = 0x%08x\n", FIDR3);
    kprintf("FIDR4  = 0x%08x\n", FIDR4);
    kprintf("FIDR5  = 0x%08x\n", FIDR5);
    kprintf("FIDR6  = 0x%08x\n", FIDR6);

    kprintf("LDCMD0 = 0x%08x\n", LDCMD0);
    kprintf("LDCMD1 = 0x%08x\n", LDCMD1);
    kprintf("LDCMD2 = 0x%08x\n", LDCMD2);
    kprintf("LDCMD3 = 0x%08x\n", LDCMD3);
    kprintf("LDCMD4 = 0x%08x\n", LDCMD4);
    kprintf("LDCMD5 = 0x%08x\n", LDCMD5);
    kprintf("LDCMD6 = 0x%08x\n", LDCMD6);

}
#endif

static void LCDClearStatusReg()
{
    // Clear the status registers by writing 1's to each bit.
    LCSR0 = ( LCD_LDD | LCD_SOF0| LCD_BER | LCD_ABC | LCD_IU0   |
                          LCD_IU1 | LCD_OU  | LCD_QD  | LCD_EOF0| LCD_BS0   |
                          LCD_SINT| LCD_RD_ST | LCD_CMD_INTR );

    LCSR1 = ( LCD_SOF1| LCD_SOF2| LCD_SOF3| LCD_SOF4| LCD_SOF5  | LCD_SOF6  |
                          LCD_EOF1| LCD_EOF2| LCD_EOF3| LCD_EOF4| LCD_EOF5  | LCD_EOF6  |
                          LCD_BS1 | LCD_BS2 | LCD_BS3 | LCD_BS4 | LCD_BS5   | LCD_BS6   |
                                    LCD_IU2 | LCD_IU3 | LCD_IU4 | LCD_IU5   | LCD_IU6 );

}

static void LcdGetDMALength(DisplayContext *pCxt, OverlayFormat *pOverlay)
{
	unsigned int pixels = pOverlay->Height * pOverlay->Width;
	unsigned int DMALength = 0;

    // Now only support COLOR_YCbCr422 & COLOR_YCbCr420 in overlay2
    assert (pOverlay->Color == COLOR_YCbCr422 || pOverlay->Color == COLOR_YCbCr420);

	if (pOverlay->Color == COLOR_YCbCr422)
	{
		DMALength = pixels;
		if ((DMALength % 4) > 0)
		{
			DMALength = (((32 - ((pixels << 3) % 32)) + (pixels << 3)) >> 3);	// 16 bits total
		}
		pCxt->uChan2Size = DMALength;

		DMALength = pixels >> 1;
		if (((pixels << 2) % 32) > 0)
		{
			DMALength = (((32 - ((pixels << 2) % 32)) + (pixels << 2)) >> 3);
		}
		pCxt->uChan3Size = DMALength;
		pCxt->uChan4Size = DMALength;
	}
	else if (pOverlay->Color == COLOR_YCbCr420)
	{
		DMALength = pixels;
		if ((DMALength % 4) > 0)
		{
			DMALength = (((32 - ((pixels << 3) % 32)) + (pixels << 3)) >> 3);	// 12 bits total
		}
		pCxt->uChan2Size = DMALength;

		DMALength = pixels >> 2;
		if (((pixels << 1) % 32) > 0)
		{
			DMALength = (((32 - ((pixels << 1) % 32)) + (pixels << 1)) >> 3);
		}
		pCxt->uChan3Size = DMALength;
		pCxt->uChan4Size = DMALength;
	}
}


void LcdCtrl_Init(DisplayContext *pCxt)
{
    int PCD = 0;
    unsigned int val_CCCR_L = 0;

    int LCLK = 0;

    int val_BPP;

    int PixelDataFormat;

    if (pCxt->basePlaneFormat == COLOR_RGB565) {
        PixelDataFormat = PDFOR_00;
    }
    else {
        PixelDataFormat = PDFOR_11; // RGBT555
    }

    LCCR0 = 0;
    LCCR1 = 0;
    LCCR2 = 0;
    LCCR3 = 0;
    LCCR4 = 0;
    LCCR5 = (LCD_SOFM1|LCD_SOFM2|LCD_SOFM3|LCD_SOFM4|LCD_SOFM5|LCD_SOFM6|
                        LCD_EOFM1|LCD_EOFM2|LCD_EOFM3|LCD_EOFM4|LCD_EOFM5|LCD_EOFM6|
                        LCD_BSM1 |LCD_BSM2 |LCD_BSM3 |LCD_BSM4 |LCD_BSM5 |LCD_BSM6 |
                        LCD_IUM1 |LCD_IUM2 |LCD_IUM3 |LCD_IUM4 |LCD_IUM5 |LCD_IUM6 );

    // Determine the frame buffer size for the DMA transfer length.
    // Scale the size based on the bpp of the frame buffer to determine
    // an actual size in bytes
    int FrameBufferSize = LCD_WIDTH * LCD_HEIGHT;
    FrameBufferSize <<= 1; // 16BPP

    // Enable the LCD
    CKEN = (CKEN & XLLP_CLKEN_MASK) | CLK_LCD ;

    LCD_FRAME_DESCRIPTOR *frameDescriptorCh0fd1 = (LCD_FRAME_DESCRIPTOR *)pCxt->vaChan0Desc;

    // Configure the general purpose frame descriptors
    // Set the physical address of the frame descriptor
    frameDescriptorCh0fd1->FDADR = LCD_FDADR(pCxt->paChan0Desc);

    // Set the physical address of the frame buffer
    frameDescriptorCh0fd1->FSADR = LCD_FSADR(pCxt->paBasePlane);

    // Clear the frame ID
    frameDescriptorCh0fd1->FIDR  = LCD_FIDR(0);

    // Set the DMA transfer length to the size of the frame buffer
    frameDescriptorCh0fd1->LDCMD = LCD_Len(FrameBufferSize);

    // Store the physical address of this frame descriptor in the frame descriptor
    frameDescriptorCh0fd1->PHYSADDR = frameDescriptorCh0fd1->FDADR;

    // FBR0 is cleared and is not used.
    FBR0 = 0;

    // Load the contents of FDADR0 with the physical address of this frame descriptor
    FDADR0 = LCD_FDADR(frameDescriptorCh0fd1->FDADR);

    // Determine the LCLK frequency programmed into the CCCR.
    // This value will be used to calculate a Pixel Clock Divisor (PCD)
    // for a given display type.
    val_CCCR_L = (CCCR & 0x0000001F);

    if (val_CCCR_L < 8) // L = [2 - 7]
        LCLK = (13 * val_CCCR_L) * 100;
    else if (val_CCCR_L < 17) // L = [8 - 16]
        LCLK = ((13 * val_CCCR_L) * 100) >> 1; // THIS
    else if (val_CCCR_L < 32) // L = [17 - 31]
        LCLK = ((13 * val_CCCR_L) * 100) >> 2;

    // Convert the bpp setting into a value that the LCD controller understands.
    val_BPP = O_BPP_16;

    // Configure the LCD Controller Control Registers
    LCCR0 = (LCD_LDM | LCD_SFM | LCD_IUM | LCD_EFM |
            LCD_PAS | LCD_QDM | LCD_BM  | LCD_OUM |
            LCD_RDSTM | LCD_CMDIM | LCD_LDDALT /*| LCD_OUC*/);

#define Samsung_LTP283QV

#ifdef Samsung_LTP283QV
#define XRES 	239             //LCD_XRES
#define YRES 	319             //LCD_YRES
#define HSW 	4   // 3           //LCD_HORIZONTAL_SYNC_PULSE_WIDTH
#define VSW 	       3       //LCD_VERTICAL_SYNC_PULSE_WIDTH
#define BLW 	10  // 8           //LCD_BEGIN_OF_LINE_WAIT_COUNT
#define BFW 	1   // 8           //LCD_BEGIN_FRAME_WAIT_COUNT
#define ELW 	20  // 8           //LCD_END_OF_LINE_WAIT_COUNT
#define EFW 	10  // 8           //LCD_END_OF_FRAME_WAIT_COUNT

			//
			// The actual equation requires that we take the ceiling of a floating point result.
			// Rather than use floats, we'll calculate an approximation to the correct PCD value
			// using integers.
			//
			PCD = (LCLK / (2 * 516));//LTM022A69A_PIXEL_CLOCK_FREQUENCY));
			// Configure the LCD Controller Control Registers
		    LCCR0 = (LCD_LDM | LCD_SFM | LCD_IUM | LCD_EFM |
								LCD_PAS | LCD_QDM | LCD_BM  | LCD_OUM |
								LCD_RDSTM | LCD_CMDIM | LCD_OUC | LCD_LDDALT);

		    LCCR1 = (LCD_PPL(XRES) | LCD_HSW(HSW) |
								LCD_ELW(ELW)  | LCD_BLW(BLW) );

		    LCCR2 = (LCD_LPP(YRES) | LCD_VSW(VSW) |
								LCD_EFW(EFW)  | LCD_BFW(BFW) );
#if 1
		    LCCR3 = (LCD_PCD(PCD)  | LCD_BPP(val_BPP) | LCD_OEP |LCD_VSP | LCD_HSP |
								LCD_PDFOR(PixelDataFormat));
#else
		    LCCR3 = (LCD_PCD(PCD)  | LCD_BPP(5) | LCD_VSP | LCD_HSP | //LCD_PCP |
								LCD_PDFOR(PixelDataFormat));
#endif
		    LCCR4 = LCD_PAL_FOR(0);
#if 0
			if ((OVL1C1 & LCD_O1EN) || (OVL2C1 & LCD_O2EN))
			{
				LCCR4 = LCD_PAL_FOR(1);
			}
#endif
//            InitLcd((XLLP_GPIO_T *)pXllpLCD->GPIO);
#endif

    LCCR4 |= LCD_K1(1) | LCD_K2(1) | LCD_K3(1);


    // Clear LCD Controller status register
    LCDClearStatusReg();
}

void LcdCtrl_Enable(DisplayContext *pCxt)
{
    LCCR0 |= LCD_ENB;
}

void LcdCtrl_Disable(DisplayContext *pCxt)
{
    LCCR0 &= ~LCD_ENB;
}

ECode LcdCtrl_SetOverlay(DisplayContext *pCxt, OverlayFormat *pOverlay)
{
    unsigned int Format;
    volatile LCD_FRAME_DESCRIPTOR *pChan2Desc, *pChan3Desc, *pChan4Desc;

    pChan2Desc = (volatile LCD_FRAME_DESCRIPTOR *)pCxt->vaChan2Desc;
	pChan3Desc = (volatile LCD_FRAME_DESCRIPTOR *)pCxt->vaChan3Desc;
	pChan4Desc = (volatile LCD_FRAME_DESCRIPTOR *)pCxt->vaChan4Desc;

	switch(pOverlay->Color)
	{
	    case COLOR_YCbCr422:
	        Format = FORMAT_PLANAR_422;
	        break;

	    case COLOR_YCbCr420:
	        Format = FORMAT_PLANAR_420;
	        break;

	    // case FORMAT_RGB:
        // case FORMAT_PACKED_444:
        // case FORMAT_PLANAR_444:
	    default:
	        kprintf("Unsupported format for overlay2 now!\n");
	        return E_NOT_IMPLEMENTED;
	}



    if ((OVL2C1) & LCD_O2EN)
	{
		kprintf("LcdCtrl_SetOverlay: Already enabled\r\n");
		return(0);
	}

	LCCR0 &= ~LCD_OUC;

    LcdGetDMALength(pCxt, pOverlay);

    // Configure the general purpose frame descriptors
	// Set the physical address of the frame descriptor
	pChan2Desc->FDADR = LCD_FDADR(pCxt->paChan2Desc);
    pChan3Desc->FDADR = LCD_FDADR(pCxt->paChan3Desc);
	pChan4Desc->FDADR = LCD_FDADR(pCxt->paChan4Desc);

	// Set the physical address of the frame buffer
	pChan2Desc->FSADR = LCD_FSADR(pCxt->paOverlay2Y);
    pChan3Desc->FSADR = LCD_FSADR(pCxt->paOverlay2Cb);
	pChan4Desc->FSADR = LCD_FSADR(pCxt->paOverlay2Cr);

	// Clear the frame ID
	pChan2Desc->FIDR  = LCD_FIDR(0);
    pChan3Desc->FIDR  = LCD_FIDR(0);
	pChan4Desc->FIDR  = LCD_FIDR(0);

	// Set the DMA transfer length to the size of the frame buffer
	pChan2Desc->LDCMD = LCD_Len(pCxt->uChan2Size);
	pChan3Desc->LDCMD = LCD_Len(pCxt->uChan3Size);
	pChan4Desc->LDCMD = LCD_Len(pCxt->uChan4Size);

	// Store the physical address of this frame descriptor in the frame descriptor
	pChan2Desc->PHYSADDR = pChan2Desc->FDADR;
    pChan3Desc->PHYSADDR = pChan3Desc->FDADR;
	pChan4Desc->PHYSADDR = pChan4Desc->FDADR;

    // FBRx is cleared and is not used.
	FBR2 = 0;
	FBR3 = 0;
	FBR4 = 0;

    unsigned int StatMask = LCD_IU2;
	if (pOverlay->Color == COLOR_YCbCr422 || pOverlay->Color == COLOR_YCbCr420)
		StatMask |= (LCD_IU3 | LCD_IU4);
	LCSR1 = StatMask; // clear

    OVL2C2 = (LCD_FOR(Format) | LCD_O2YPOS(pOverlay->PosY) | LCD_O2XPOS(pOverlay->PosX));
    OVL2C1 = (LCD_O2EN | LCD_BPP2(pOverlay->Bpp) | LCD_LPO2(pOverlay->Height-1) | LCD_PPL2(pOverlay->Width-1));

    // Wait for the hardware to signal input FIFOx underrun
	// before setting the FDADRx regs (and hence enabling DMA).
	// This is a key part of the hardware workaround described in Tibet sighting 49219.
	// Note that that IUx doesn't happen the first time after reset or resume.
	// The underrun apparently does occur in the controller but the controller doesn't set IUx.
	// According HW eng, the 50ms OSTDelay introduced by waiting for IUx should be sufficient
	// to avoid the hardware problem.
	// Note also that this may only needed for YUV modes but also works for RGB modes and
	// implementing it for all modes produces less convoluted code.
	DzDelay(50*1000);

	// Load the FDADRx regs with the physical address of the frame descriptors
	// Testing indicates that this is what actually enables DMA transfers.
	// If FDADRx isn't set (even if already set to the desired value) the DMA doesn't start.
    FDADR2 = LCD_FDADR(pChan2Desc->FDADR);
	if (pOverlay->Color == COLOR_YCbCr422 || pOverlay->Color == COLOR_YCbCr420) {
	    FDADR3 = LCD_FDADR(pChan3Desc->FDADR);
	    FDADR4 = LCD_FDADR(pChan4Desc->FDADR);
	}

	LCSR1 = StatMask; // clear

    return NOERROR;
}

void LcdCtrl_DisableOverlay(DisplayContext *pCxt, int layer)
{
    unsigned int OL2Format;
	unsigned int StatMask = 0;
    volatile LCD_FRAME_DESCRIPTOR *pChan2Desc, *pChan3Desc, *pChan4Desc;
    OverlayFormat *pOverlay;

    assert(layer == 2); // Now only support overlay2

    pOverlay = &pCxt->overlay[layer];

    pChan2Desc = (volatile LCD_FRAME_DESCRIPTOR *)pCxt->vaChan2Desc;
	pChan3Desc = (volatile LCD_FRAME_DESCRIPTOR *)pCxt->vaChan3Desc;
	pChan4Desc = (volatile LCD_FRAME_DESCRIPTOR *)pCxt->vaChan4Desc;

	if (!(OVL2C1 & LCD_O2EN)) {
		// Not enable
		return;
	}

    OL2Format = (OVL2C2 >> 20) & 7;
	StatMask = LCD_BS2;
	if (OL2Format != FORMAT_RGB)
		StatMask |= (LCD_BS3 | LCD_BS4);
    LCSR1 = StatMask;

    OVL2C1 &= 0x00FFFFFF; // Clear the enable bit, and clear the reserved bits 30:24.

    // Wait for DMA branch taken status (BSx bits to be set).
	// This is per HW Eng. workaround described in Tibet sighting 49219.
	// Note that testing indicates the loading of FBRx avoids the hardware problem.
	// Not loading FBRx and just waiting for 50ms does not avoid the problem.
    FBR2 = LCD_FDADR(pChan2Desc->FDADR) | (LCD_BRA | LCD_BINT);
	if (pOverlay->Color == COLOR_YCbCr422 || pOverlay->Color == COLOR_YCbCr420) {
        FBR3 = LCD_FDADR(pChan3Desc->FDADR) | (LCD_BRA | LCD_BINT);
	    FBR4 = LCD_FDADR(pChan4Desc->FDADR) | (LCD_BRA | LCD_BINT);
	}

    DzDelay(200 * 1000);
}

void LcdCtrl_Suspend(DisplayContext *pCxt, int type)
{
    if (LCCR0 & LCD_ENB){
	    switch (type)	{
	        case 0: // Gracefullly
	    	    // Initiate power down sequence
	    	    LCCR0 |= LCD_DIS;
	    	    // Wait for LDD bit to get set once the last DMA transfer has completed
	    	    DzDelay(50*1000);
	    	    while(!(LCSR0 & LCD_LDD)) {
	    		    kprintf("lcd deadloop in XllpLCDSuspend FDADR0 %x FSADR0 %x FIDR0 %x LDCMD0 %x \n",
	    				FDADR0, FSADR0, FIDR0, LDCMD0);
	    	    }

	    	    // Clear the sticky LDD bit
	    	    LCSR0 |= LCD_LDD;
	    	    break;

	        case 1: // Immediately
	    	    LCCR0 &= ~LCD_ENB;
	    	    break;

	        default :
	    	    break;
	    }
    }
}


void LcdCtrl_Resume(DisplayContext *pCxt)
{
    LcdCtrl_Init(pCxt);
    LcdCtrl_Enable(pCxt);
}

void LcdCtrl_SetTransLevel(DisplayContext *pCxt, BYTE level)
{
    level = 7 - level / 32; // [0, 255] -> [7, 0]

    // Clear K1, K2, K3 bits
    LCCR4 &= ~(LCD_K1(7) | LCD_K2(7) | LCD_K3(7));

    LCCR4 |= LCD_K1(level) | LCD_K2(level) | LCD_K3(level);

}
